#pragma once

#include <vector>

#ifdef PUNCH_MSGPACK
#include <msgpack.hpp>
#endif

namespace mdtx::punch::tradesim
{
    /**
     * @brief Records instrument market data
     *
     * @tparam QT quote type
     */
    template <typename QT>
    class MktPool
    {
    public:
        using time_type = typename QT::time_type;
        using id_type = typename QT::id_type;
        using quant_type = typename QT::quant_type;

        /**
         * @brief Market liquidity pool
         *
         * @details a pool is future market data records: a point of time, a ticker price,
         * traded volume, traded turnover, highest tradable price, lowest tradable price,
         * and number of traded orders
         *
         */
        struct MktPoolT
        {
            time_type time;
            quant_type price, vol, tnvr, high, low, norder;

            bool avail() const
            {
                return vol > 0 && tnvr > 0 && norder != 0;
            }

#ifdef PUNCH_MSGPACK
            MSGPACK_DEFINE_ARRAY(time, price, vol, tnvr, high, low, norder);
#endif
        };

    private:
        size_t n;
        std::vector<bool> vinit;
        std::vector<MktPoolT> val, val0;

    public:
        explicit MktPool(size_t n)
            : n(n), vinit(n), val(n), val0(n)
        {
        }

        size_t num_instr() const { return n; }

        bool init(id_type ins_id) const { return vinit[ins_id]; }

        auto &get(id_type ins_id) { return val[ins_id]; }
        const auto &get(id_type ins_id) const { return val[ins_id]; }
        auto &get0(id_type ins_id) { return val0[ins_id]; }
        const auto &get0(id_type ins_id) const { return val0[ins_id]; }

        /**
         * @brief Set the martket pool to a BBO tick
         *
         * @param ins_id instrument id
         * @param time time
         * @param price price
         * @param vol volume
         * @param tnvr turnover
         * @param norder number of order
         * @param bid_bbo bid BBO
         * @param ask_bbo ask BBO
         * @return MktPoolT&
         */
        auto &set_tick(id_type ins_id, time_type time, quant_type price, quant_type vol, quant_type tnvr,
                       quant_type norder, quant_type bid_bbo, quant_type ask_bbo)
        {
            auto &pool = val[ins_id];
            pool.time = time;
            pool.price = price;
            pool.vol = vol;
            pool.tnvr = tnvr;
            pool.high = ask_bbo;
            pool.low = bid_bbo;
            pool.norder = norder;
            if (!vinit[ins_id])
            {
                val0[ins_id] = pool;
                vinit[ins_id] = true;
            }
            return pool;
        }

        /**
         * @brief Set the market pool to OHLCV bar
         *
         * @param ins_id instrument id
         * @param time time
         * @param high high
         * @param low low
         * @param vol volume
         * @param tnvr turnover
         * @param vwap VWAP
         * @param norder number of order
         * @return MktPoolT&
         */
        auto &set_bar(id_type ins_id, time_type time, quant_type high, quant_type low, quant_type vol, quant_type tnvr,
                      quant_type vwap, quant_type norder)
        {
            auto &pool = get(ins_id);
            pool.time = time;
            pool.high = high;
            pool.low = low;
            pool.price = vwap;
            pool.vol = vol;
            pool.tnvr = tnvr;
            pool.norder = norder;
            if (!vinit[ins_id])
            {
                val0[ins_id] = pool;
                vinit[ins_id] = true;
            }
            return pool;
        }

        /**
         * @brief Set the market pool to OHLCV bar
         *
         * @param ins_id instrument id
         * @param time time
         * @param high high
         * @param low low
         * @param vol volume
         * @param tnvr turnover
         * @param vwap VWAP
         * @return MktPoolT&
         */
        auto &set_bar(id_type ins_id, time_type time, quant_type high, quant_type low, quant_type vol, quant_type tnvr, quant_type vwap)
        {
            return set_bar(ins_id, time, high, low, vol, tnvr, vwap, quant_type{} - 1);
        }

        auto &set_raw(id_type ins_id, const MktPoolT &pool)
        {
            val[ins_id] = pool;
            if (!vinit[ins_id])
            {
                val0[ins_id] = pool;
                vinit[ins_id] = true;
            }
            return val[ins_id];
        }

        void set_time(id_type ins_id, time_type time)
        {
            val[ins_id].time = time;
        }

        void set_time(time_type time)
        {
            for (auto &pool : val)
            {
                pool.time = time;
            }
        }

        void freeze(id_type ins_id)
        {
            val[ins_id].norder = 0;
        }

        void freeze()
        {
            for (auto &pool : val)
            {
                pool.norder = 0;
            }
        }
    };
}
